
// Testing Sebastian Aaltonen's soft shadow improvement
//
// The technique is based on estimating a better closest point in ray
// at each step by triangulating from the previous march step.
//
// More info about the technique at slide 39 of this presentation:
// https://www.dropbox.com/s/s9tzmyj0wqkymmz/Claybook_Simulation_Raytracing_GDC18.pptx?dl = 0
//
// Traditional technique: http://iquilezles.org/www/articles/rmshadows/rmshadows.htm
//
// Go to lines 54 to compare both.


// make this 1 is your machine is too slow
#define AA 2

//------------------------------------------------------------------
// y = 0 平面
float sdPlane( vec3 p ) {
    return p.y;
}
// 长方体
float sdBox( vec3 p, vec3 b ) {
    vec3 d = abs(p) - b;
    return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0));
}
//------------------------------------------------------------------
// 与场景求距离
float map( in vec3 pos ) {
    
    return min( // 使用min执行两个图元的并集
        sdPlane(pos.xyz-vec3( 0.0, 0.00, 0.0)), // y = 0 平面
        sdBox(pos.xyz-vec3( 0.0, 0.25, 0.0), vec3(0.2, 0.5, 0.2) ) // 在( 0.0, 0.25, 0.0)处放置尺寸为(0.2, 0.5, 0.2)长方体
        );
}

//------------------------------------------------------------------
// ro 射线命中的点
// rd 点到灯光的方向
// mint 最小值
// maxt 最大值
float shadow( in vec3 ro, in vec3 rd, float mint, float maxt )
{
    for( float t=mint; t<maxt; )
    {
        // 沿着光源方向推进，直到与场景碰撞
        float h = map(ro + rd*t);
        if( h<0.001 )
            return 0.0; // 发生碰撞，不接受光照
        t += h;
    }
    return 1.0; // 未发生碰撞，接收光照
}

// 计算法线
// https://iquilezles.org/www/articles/normalsSDF/normalsSDF.htm
vec3 calcNormal( in vec3 pos ) {

    // 取4个方向偏移求取法线
    const float h = 0.0001; // replace by an appropriate value
    const vec2 k = vec2(1.0,-1.0);
    return normalize( k.xyy*map( pos + k.xyy*h ) + 
                      k.yyx*map( pos + k.yyx*h ) + 
                      k.yxy*map( pos + k.yxy*h ) + 
                      k.xxx*map( pos + k.xxx*h ) );
}

// 发射射线
float castRay( in vec3 ro, in vec3 rd ) {
    float tmin = 1.0;
    float tmax = 20.0;
    
    float t = tmin;
    for( int i = 0; i<64; i++ ) { // 限制最大迭代次数
        float precis = 0.0005*t; // 前进幅度，如果过小则退出循环
        float res = map( ro+rd*t ); // 求取与场景最近距离
        if( res<precis || t>tmax ) break;
        t += res; // 以与场景最近距离作为推进步长
    }
    if( t>tmax ) t = -1.0;
    return t;
}

// 渲染
// ro 射线起点
// rd 射线方向
vec3 render( in vec3 ro, in vec3 rd) {
    vec3  col = vec3(0.0);
    float t = castRay(ro, rd);
    if( t>-0.5 ) { // t 为正值
        vec3 pos = ro + t*rd;
        vec3 nor = calcNormal( pos );
        
        // material
        vec3 mate = vec3(0.3,0.3,0.3); // 材质颜色
        
        // key light
        vec3  lig = normalize( vec3(-0.1, 0.3, 0.6) ); // 方向光照射方向
        vec3  hal = normalize( lig-rd ); 
        float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); // 计算diffuse
        float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ), 16.0); // 计算高光

        col = (mate * dif + spe) * vec3(1.00, 0.70, 0.5) * shadow( pos, lig, 0.01, 3.0); // 计算光照
        
        // fog
        col *= exp( -0.0005*t*t*t );
    }
    return col;
}

// ro 摄像机位置
// ta 看向的位置
// 摄像机翻转角度，默认朝上。
mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) {
    vec3 cw = normalize(ta-ro); // 摄像机坐标到看向的位置作为Z轴
    vec3 cp = vec3(sin(cr), cos(cr), 0.0); // UP方向
    vec3 cu = normalize( cross(cw, cp) );   // 计算X轴
    vec3 cv = normalize( cross(cu, cw) );   // 计算Y轴
    return mat3( cu, cv, cw );
}

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    // camera	
    float an = 12.0 - sin(0.1*iTime);
    vec3 ro = vec3( 3.0*cos(0.1*an), 1.0, -3.0*sin(0.1*an) ); // 摄像机位置
    vec3 ta = vec3( 0.0, -0.4, 0.0 ); // 看向位置
    // camera-to-world transformation
    
    mat3 ca = setCamera( ro, ta, 0.0 );
    vec3 tot = vec3(0.0); // 总颜色值、用于处理锯齿
    #if AA>1
        for( int m = 0; m<AA; m++ )
        for( int n = 0; n<AA; n++ ) {
            // pixel coordinates
            vec2 o = vec2(float(m), float(n)) / float(AA) - 0.5;
            vec2 p = (-iResolution.xy + 2.0*(fragCoord+o))/iResolution.y;
    #else    
            vec2 p = (-iResolution.xy + 2.0*fragCoord)/iResolution.y; // 像素坐标-> [-1,1]
    #endif
        
        // 射线方向
        vec3 rd = ca * normalize( vec3(p.xy, 2.0) );
        
        // render
        vec3 col = render( ro, rd);
        
        tot += col;
    #if AA>1
        }
        tot /= float(AA*AA);
    #endif
    
    
    fragColor = vec4( tot, 1.0 );
}